﻿using System;
using ActiproSoftware.SyntaxEditor;
using ActiproSoftware.SyntaxEditor.Addons.Dynamic;

namespace LevenBlog.Core.CodeHighlighter {
    /// <summary>
    /// Provides an implementation of a <c>VB.NET</c> syntax language that can perform automatic outlining.
    /// </summary>
    public class VBDotNetDynamicSyntaxLanguage : DynamicOutliningSyntaxLanguage {

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // OBJECT
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// This constructor is for designer use only and should never be called by your code.
        /// </summary>
        public VBDotNetDynamicSyntaxLanguage() {
        }
        //New

        /// <summary>
        /// Initializes a new instance of the <c>VBDotNetDynamicSyntaxLanguage</c> class. 
        /// </summary>
        /// <param name="key">The key of the language.</param>
        /// <param name="secure">Whether the language is secure.</param>
        public VBDotNetDynamicSyntaxLanguage(string key, bool secure)
            : base(key, secure) {
        }
        //New

        /////////////////////////////////////////////////////////////////////////////////////////////////////
        // PUBLIC PROCEDURES
        /////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Returns token parsing information for automatic outlining that determines if the current <see cref="IToken"/>
        /// in the <see cref="TokenStream"/> starts or ends an outlining node.
        /// </summary>
        /// <param name="tokenStream">A <see cref="TokenStream"/> that is positioned at the <see cref="IToken"/> requiring outlining data.</param>
        /// <param name="outliningKey">Returns the outlining node key to assign. A <see langword="null"/> should be returned if the token doesn't start or end a node.</param>
        /// <param name="tokenAction">Returns the <see cref="OutliningNodeAction"/> to take for the token.</param>
        public override void GetTokenOutliningAction(TokenStream tokenStream, ref string outliningKey, ref OutliningNodeAction tokenAction) {
            // Get the token
            IToken token = tokenStream.Peek();

            // See if the token starts or ends an outlining node
            switch (token.Key) {
                case "XMLCommentStartToken":
                    outliningKey = "XMLComment";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "XMLCommentEndToken":
                    outliningKey = "XMLComment";
                    tokenAction = OutliningNodeAction.End;
                    break;
                case "SubReservedWordToken":
                    outliningKey = "SubBlock";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "EndSubReservedWordToken":
                    outliningKey = "SubBlock";
                    tokenAction = OutliningNodeAction.End;
                    break;
                case "FunctionReservedWordToken":
                    outliningKey = "FunctionBlock";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "EndFunctionReservedWordToken":
                    outliningKey = "FunctionBlock";
                    tokenAction = OutliningNodeAction.End;
                    break;
                case "PropertyReservedWordToken":
                    outliningKey = "PropertyBlock";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "EndPropertyReservedWordToken":
                    outliningKey = "PropertyBlock";
                    tokenAction = OutliningNodeAction.End;
                    break;
                case "ClassReservedWordToken":
                    outliningKey = "ClassBlock";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "EndClassReservedWordToken":
                    outliningKey = "ClassBlock";
                    tokenAction = OutliningNodeAction.End;
                    break;
                case "InterfaceReservedWordToken":
                    outliningKey = "InterfaceBlock";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "EndInterfaceReservedWordToken":
                    outliningKey = "InterfaceBlock";
                    tokenAction = OutliningNodeAction.End;
                    break;
                case "EnumReservedWordToken":
                    outliningKey = "EnumBlock";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "EndEnumReservedWordToken":
                    outliningKey = "EnumBlock";
                    tokenAction = OutliningNodeAction.End;
                    break;
                case "StructureReservedWordToken":
                    outliningKey = "StructureBlock";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "EndStructureReservedWordToken":
                    outliningKey = "StructureBlock";
                    tokenAction = OutliningNodeAction.End;
                    break;
                case "ModuleReservedWordToken":
                    outliningKey = "ModuleBlock";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "EndModuleReservedWordToken":
                    outliningKey = "ModuleBlock";
                    tokenAction = OutliningNodeAction.End;
                    break;
                case "NamespaceReservedWordToken":
                    outliningKey = "NamespaceBlock";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "EndNamespaceReservedWordToken":
                    outliningKey = "NamespaceBlock";
                    tokenAction = OutliningNodeAction.End;
                    break;
                case "RegionPreProcessorDirectiveStartToken":
                    outliningKey = "RegionPreProcessorDirective";
                    tokenAction = OutliningNodeAction.Start;
                    break;
                case "EndRegionPreProcessorDirectiveStartToken":
                    outliningKey = "RegionPreProcessorDirective";
                    tokenAction = OutliningNodeAction.End;
                    break;
            }
        }
        //GetTokenOutliningAction

        /// <summary>
        /// Occurs after automatic outlining is performed on a <see cref="Document"/> that uses this language.
        /// </summary>
        /// <param name="document">The <see cref="Document"/> that is being modified.</param>
        /// <param name="e">A <c>DocumentModificationEventArgs</c> that contains the event data.</param>
        /// <remarks>
        /// A <see cref="DocumentModification"/> may or may not be passed in the event arguments, depending on if the outlining
        /// is performed in the main thread.
        /// </remarks>
        protected override void OnDocumentAutomaticOutliningComplete(Document document, DocumentModificationEventArgs e) {
            // Collapse all outlining region nodes
            document.Outlining.RootNode.CollapseDescendants("RegionPreProcessorDirective");
        }
        //OnDocumentAutomaticOutliningComplete

        /// <summary>
        /// Allows for setting the collapsed text for the specified <see cref="OutliningNode"/>.
        /// </summary>
        /// <param name="node">The <see cref="OutliningNode"/> that is requesting collapsed text.</param>
        public override void SetOutliningNodeCollapsedText(OutliningNode node) {
            TokenCollection tokens = node.Document.Tokens;
            int tokenIndex = tokens.IndexOf(node.StartOffset);
            string tokenKey = tokens[tokenIndex].Key;

            switch (tokenKey) {
                case "XMLCommentStartToken":
                    node.CollapsedText = "/**/";
                    break;
                case "FunctionReservedWordToken":
                case "PropertyReservedWordToken":
                case "SubReservedWordToken":
                case "ClassReservedWordToken":
                case "InterfaceReservedWordToken":
                case "EnumReservedWordToken":
                case "StructureReservedWordToken":
                case "ModuleReservedWordToken":
                case "NamespaceReservedWordToken":
                    string collapsedText = string.Empty;
                    tokenIndex = tokenIndex + 1;
                    while (tokenIndex < tokens.Count) {
                        if (!(tokens[tokenIndex].IsWhitespace)) {
                            switch (tokens[tokenIndex].Key) {
                                case "LineTerminatorToken":
                                case "OpenParenthesisPatternGroup":
                                    break; // TODO: might not be correct. Was : Exit Select
                                default:
                                    collapsedText = tokens.Document.GetTokenText(tokens[tokenIndex]);
                                    break; // TODO: might not be correct. Was : Exit While
                            }
                        }
                        tokenIndex = tokenIndex + 1;
                    }


                    switch (tokenKey) {
                        case "FunctionReservedWordToken":
                            node.CollapsedText = string.Format("Function {0}()", collapsedText.Trim());
                            break;
                        case "PropertyReservedWordToken":
                            node.CollapsedText = string.Format("Property {0}()", collapsedText.Trim());
                            break;
                        case "SubReservedWordToken":
                            node.CollapsedText = string.Format("Sub {0}()", collapsedText.Trim());
                            break;
                        case "ClassReservedWordToken":
                            node.CollapsedText = string.Format("Class {0}", collapsedText.Trim());
                            break;
                        case "InterfaceReservedWordToken":
                            node.CollapsedText = string.Format("Interface {0}", collapsedText.Trim());
                            break;
                        case "EnumReservedWordToken":
                            node.CollapsedText = string.Format("Enum {0}", collapsedText.Trim());
                            break;
                        case "StructureReservedWordToken":
                            node.CollapsedText = string.Format("Structure {0}", collapsedText.Trim());
                            break;
                        case "ModuleReservedWordToken":
                            node.CollapsedText = string.Format("Module {0}", collapsedText.Trim());
                            break;
                        case "NamespaceReservedWordToken":
                            node.CollapsedText = string.Format("Namespace {0}", collapsedText.Trim());
                            break;
                    }
                    break;
                case "RegionPreProcessorDirectiveStartToken":
                    collapsedText = string.Empty;
                    tokenIndex = tokenIndex + 1;
                    while (tokenIndex < tokens.Count) {
                        if (tokens[tokenIndex].Key == "PreProcessorDirectiveEndToken") {
                            break; // TODO: might not be correct. Was : Exit While
                        }
                        collapsedText += tokens.Document.GetTokenText(tokens[tokenIndex]);
                        tokenIndex = tokenIndex + 1;
                    }

                    collapsedText = collapsedText.Trim();
                    if (collapsedText.StartsWith("\"")) {
                        collapsedText = collapsedText.Substring(1);
                    }

                    if (collapsedText.EndsWith("\"")) {
                        collapsedText = collapsedText.Substring(0, collapsedText.Length - 1);
                    }

                    if (collapsedText.Length == 0) {
                        collapsedText = "...";
                    }

                    node.CollapsedText = collapsedText.Trim();
                    break;
            }
        }
        //SetOutliningNodeCollapsedText
    }
    //VBDotNetDynamicSyntaxLanguage
}
